package com.xmh.autocode.main;

//import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.xmh.myutil.util.FileUtil;
import org.junit.Test;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.*;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 代码自动生成
 *
 * @author 帅哥
 */
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("/spring_bean.xml")
//@Transactional
public class AutomaticCodeGenerationBySSM {
    /*
     * -==============-=-=================-==============-==========================
     * -============== =====================控制台==================================
     * -==============-=-=================-==============-==========================
     */
    private final String vo_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\entity\\";

    private final String controller_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\controller\\system\\";

    private final String service_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\service\\";

    private final String dao_Path = "F:\\eclipseWord\\skyCash\\src\\com\\out\\dao\\";

    private final String xml_Path = "F:\\eclipseWord\\skyCash\\resources\\mybatis\\system\\";

    private final String[] table_Name_List = {"t_video"};
    /*
     * -==============-=-=================-==============-==========================
     * -============== =============================================================
     * -==============-=-=================-==============-==========================
     */

//    @Resource(name = "dataSource")
    private DataSource comboPooledDataSource;

    private final String controle_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/SSM/controleModel.model").getPath());

    private final String service_Impl_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/SSM/serviceImplementsModel.model").getPath());

    private final String service_Inte_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/SSM/serviceInterfaceModel.model").getPath());

    private final String dao_Inte_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/SSM/daoInterface.model").getPath());

    private final String dao_Impl_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/SSM/daoInterfaceImpl.model").getPath());

    @Test
    public void create() throws Exception {
        List<Object> tableList = selectTalbeDetailed(table_Name_List);

        createVo(tableList);
        createXml(tableList);
        createMode(tableList, dao_Inte_Code, dao_Path, "DAO");
        createMode(tableList, dao_Impl_Code, dao_Path, "DAOImpl");
        createMode(tableList, controle_Code, controller_Path, "Controller");
        createMode(tableList, service_Inte_Code, service_Path, "Service");
        createMode(tableList, service_Impl_Code, service_Path, "ServiceImpl");
    }

    /**
     * 根据模板文件创建对应的文件以及对应路径
     *
     * @param tableList
     * @param modeCode
     * @param path
     * @param nameSuffix
     */
    @SuppressWarnings("unchecked")
    private void createMode(List<Object> tableList, String modeCode, String path, String nameSuffix) {
        for (Object object : tableList) {
            Map<String, Object> tableMap = (Map<String, Object>) object;
            // 转为驼峰
            String voName = nameTransformation((String) tableMap.get("tableName"));
            // 首字母大写
            String className = titleCase(voName);

            String fileName = className + nameSuffix;

            String filePath = path + voName + (fileName.substring(fileName.length() - 4).equals("Impl") ? "\\impl\\" : "");

            String code = extracted(voName, className, modeCode);

            outFile(code, filePath, fileName, "java");
        }
    }

    /**
     * 替换对应的包名-类型，以及表名
     *
     * @param voName
     * @param className
     * @param code
     * @return
     */
    private String extracted(String voName, String className, String code) {
        String nowStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

        String replace = code.replace("__LowercaseTtableName__", voName);
        replace = replace.replace("__UpperCaseTtableName__", className);
        replace = replace.replace("____Date____", nowStr);
        replace = replace.replace("___EPath___", getPackagePath(vo_Path));
        replace = replace.replace("__Service__", getPackagePath(service_Path));
        replace = replace.replace("__DAO__", getPackagePath(dao_Path));
        replace = replace.replace("__Controller__", getPackagePath(controller_Path));

        return replace;
    }

    /**
     * 根据文件相对路径获取包路径
     *
     * @param path
     * @return
     */
    private String getPackagePath(String path) {
        String str = path.split("src")[1];
        String packPach = str.substring(1, str.length() - 1).replace("\\", ".");
        return packPach;
    }

    /**
     * 创建xml文件
     *
     * @param tableNameMaplist
     */
    @SuppressWarnings("unchecked")
    private void createXml(List<Object> tableNameMaplist) {
        for (Object object : tableNameMaplist) {
            Map<String, Object> tableMap = (Map<String, Object>) object;

            String tableName = (String) tableMap.get("tableName");
            // 转为驼峰
            String voName = nameTransformation(tableName);
            // 首字母大写
            String className = titleCase(voName);

            StringBuffer asColumn = new StringBuffer();
            StringBuffer ifLabel = new StringBuffer();
            StringBuffer addColumnUp = new StringBuffer();
            StringBuffer addColumnDown = new StringBuffer();
            StringBuffer updateSetLabelSb = new StringBuffer();

            List<Object> list = (List<Object>) tableMap.get("columnList");
            for (int i = 0; i < list.size(); i++) {
                Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

                String columnName = (String) columnMap.get("columnName");

                String type = (String) columnMap.get("type");

                // 字段名转为驼峰
                String cName = nameTransformation(columnName);

                if (i == list.size() - 1) {
                    asColumn.append("			" + columnName + " AS " + cName + "\n");
                    addColumnUp.append("			" + columnName + ")\n");
                    addColumnUp.append("		VALUES ( \n");
                    addColumnDown.append("			#{" + cName + "})\n");
                } else {
                    asColumn.append("			" + columnName + " AS " + cName + ",\n");
                    addColumnUp.append("			" + columnName + ",\n");
                    addColumnDown.append("			#{" + cName + "},\n");
                }

                String ifStartBelan = null;
                if ("VARCHAR".equals(type)) {
                    ifStartBelan = "			<if test=\"" + cName + " != null and " + cName + " != '' \">\n";
                } else {
                    ifStartBelan = "			<if test=\"" + cName + " != null \">\n";
                }
                String andString = "				and " + columnName + " = #{" + cName + "} \n";
                String ifEndBelan = "			</if>\n";

                ifLabel.append(ifStartBelan);
                ifLabel.append(andString);
                ifLabel.append(ifEndBelan);

                if (!columnName.equals("id")) {
                    updateSetLabelSb.append(ifStartBelan);
                    updateSetLabelSb.append("				" + columnName + " = #{" + cName + "},  \n");
                    updateSetLabelSb.append(ifEndBelan);
                }
            }
            StringBuffer xmlFileSb = new StringBuffer();

            xmlFileSb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            xmlFileSb.append("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n");
            xmlFileSb.append("		\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n");
            xmlFileSb.append("<mapper namespace=\"__UpperCaseTtableName__XML\">\n");
            xmlFileSb.append("\n");
            xmlFileSb.append(
                    "	<select id=\"get__UpperCaseTtableName__\" parameterType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\" resultType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\">\n");
            xmlFileSb.append("		SELECT \n");
            xmlFileSb.append(asColumn);
            xmlFileSb.append("		FROM _____TableName______ \n");
            xmlFileSb.append("		<where>\n");
            xmlFileSb.append(ifLabel);
            xmlFileSb.append("		</where>\n");
            xmlFileSb.append("	</select>\n\n");
            xmlFileSb.append(
                    "	<insert id=\"add__UpperCaseTtableName__\"  parameterType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n");
            xmlFileSb.append("		INSERT INTO _____TableName______ (\n");
            xmlFileSb.append(addColumnUp);
            xmlFileSb.append(addColumnDown);
            xmlFileSb.append("	</insert>\n\n");
            xmlFileSb.append(
                    "	<delete id=\"delete__UpperCaseTtableName__\" parameterType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\">\n");
            xmlFileSb.append("		DELETE FROM _____TableName______\n");
            xmlFileSb.append("		<where>\n");
            xmlFileSb.append(ifLabel);
            xmlFileSb.append("		</where>\n");
            xmlFileSb.append("	</delete>\n\n");
            xmlFileSb.append(
                    "	<update id=\"update__UpperCaseTtableName__\" parameterType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\">\n");
            xmlFileSb.append("		UPDATE _____TableName______\n");
            xmlFileSb.append("		<set>\n");
            xmlFileSb.append(updateSetLabelSb);
            xmlFileSb.append("		</set>\n");
            xmlFileSb.append("		where id = #{id}\n");
            xmlFileSb.append("	</update>\n\n");
            xmlFileSb.append(
                    "	<select id=\"select__UpperCaseTtableName__\" parameterType=\"java.util.Map\" resultType=\"___EPath___.__LowercaseTtableName__.__UpperCaseTtableName__Entity\">\n");
            xmlFileSb.append("		SELECT \n");
            xmlFileSb.append(asColumn);
            xmlFileSb.append("		FROM _____TableName______ \n");
            xmlFileSb.append("		<where>\n");
            xmlFileSb.append(ifLabel);
            xmlFileSb.append("		</where>\n");
            xmlFileSb.append("		<if test=\"pageIndex != null \">\n");
            xmlFileSb.append("			<if test=\"pageNumber != null \">\n");
            xmlFileSb.append("				LIMIT #{pageIndex}, #{pageNumber}\n");
            xmlFileSb.append("			</if>\n");
            xmlFileSb.append("		</if>\n");
            xmlFileSb.append("	</select>\n");
            xmlFileSb.append("</mapper>");

            String xmlCode = xmlFileSb.toString();

            xmlCode = extracted(voName, className, xmlCode);

            xmlCode = xmlCode.replace("_____TableName______", tableName);

            outFile(xmlCode, xml_Path, className + "XML", "xml");
        }
    }

    /**
     * 创建实体类
     *
     * @param tableNameMaplist
     */
    @SuppressWarnings("unchecked")
    private void createVo(List<Object> tableNameMaplist) {
        for (Object object : tableNameMaplist) {
            Map<String, Object> tableMap = (Map<String, Object>) object;

            String tableName = (String) tableMap.get("tableName");
            // 转为驼峰
            String voName = nameTransformation(tableName);
            // 首字母大写
            String className = titleCase(voName) + "Entity";

            StringBuffer voClassFileSb = new StringBuffer();
            StringBuffer columnSb = new StringBuffer();
            StringBuffer getSetSb = new StringBuffer();
            StringBuffer getVo = new StringBuffer();
            StringBuffer getMap = new StringBuffer();
            StringBuffer isEmptySb = new StringBuffer();

            voClassFileSb.append("package " + getPackagePath(vo_Path) + "." + voName + ";\n\n");
            voClassFileSb.append("import java.util.Map;\n");
            voClassFileSb.append("import java.util.Date;\n");
            voClassFileSb.append("import java.util.HashMap;\n");
            voClassFileSb.append("import java.math.BigDecimal;\n");
            voClassFileSb.append("import java.sql.Timestamp;\n");
            voClassFileSb.append("import com.alibaba.fastjson.JSONObject;\n");
            voClassFileSb.append("import java.io.Serializable;\n\n");
            voClassFileSb.append("public class " + className + " implements Serializable {\n\n");

            getVo.append("	public " + className + "(Map<String, Object> map) {\n");
            getVo.append("		if (map == null || map.isEmpty()) {\n");
            getVo.append("			return;\n");
            getVo.append("		}\n\n");

            getMap.append("\n	public Map<String, Object> toMap(){\n");
            getMap.append("		Map<String, Object> map = new HashMap<String, Object>();\n\n");

            isEmptySb.append("	public boolean isEmpty() {\n");

            List<Object> list = (List<Object>) tableMap.get("columnList");
            for (int i = 0; i < list.size(); i++) {
                Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

                String columnName = (String) columnMap.get("columnName");
                // 字段名转为驼峰
                String cName = nameTransformation(columnName);

                // 字段名转为驼峰后首字母大写
                String titleCaseCname = titleCase(cName);

                // 转为对应的java类型
                String javaType = typeTransformation((String) columnMap.get("type"));

                String remarks = (String) columnMap.get("remarks");

                columnSb.append("	/**\n	 *____remarks____\n	 */\n");
                columnSb.append("	private ____javaType____ ____cName____;\n\n");

                getSetSb.append("	/**\n");
                getSetSb.append("	 * ____remarks____\n");
                getSetSb.append("	 */\n");
                getSetSb.append("	public ____javaType____ get____ClassName____() {\n");
                getSetSb.append("		return ____cName____;\n");
                getSetSb.append("	}\n\n");
                getSetSb.append("	/**\n");
                getSetSb.append("	 * ____remarks____\n");
                getSetSb.append("	 */\n");
                getSetSb.append("	public void set____ClassName____(____javaType____ ____cName____) {\n");
                getSetSb.append("		this.____cName____ = ____cName____;\n");
                getSetSb.append("	}\n\n");

                if ("String".equals(javaType)) {
                    getVo.append("		____cName____ = (String) map.get(\"____cName____\");\n\n");
                } else if ("Date".equals(javaType)) {
                    getVo.append("		Object ____cName____obj = map.get(\"____cName____\");\n"
                            + "		if (____cName____obj != null) {\n"
                            + "			if (____cName____obj instanceof Date) {\n"
                            + "				____cName____ = (Date) ____cName____obj;\n" + "			} else {\n"
                            + "				____cName____ = new Date((long) ____cName____obj);\n" + "			}\n"
                            + "		}\n\n");
                } else if ("BigDecimal".equals(javaType)) {
                    getVo.append("		if (map.get(\"____cName____\") != null) {\n");
                    getVo.append(
                            "			____cName____ = new BigDecimal(String.valueOf(map.get(\"____cName____\")));\n");
                    getVo.append("		}\n\n");
                } else {
                    getVo.append("		if (map.get(\"____cName____\") != null) {\n");
                    getVo.append(
                            "			____cName____ = ____javaType____.valueOf(String.valueOf(map.get(\"____cName____\")));\n");
                    getVo.append("		}\n\n");
                }
                getMap.append("		map.put(\"____cName____\", ____cName____);\n");

                isEmptySb.append("		if (____cName____ != null) {\n");
                isEmptySb.append("			return false;\n");
                isEmptySb.append("		}\n");

                isEmptySb = new StringBuffer(isEmptySb.toString().replace("____cName____", cName));
                getMap = new StringBuffer(getMap.toString().replace("____cName____", cName));

                String voStr = getVo.toString();
                voStr = voStr.replace("____cName____", cName);
                voStr = voStr.replace("____javaType____", javaType);
                getVo = new StringBuffer(voStr);

                String columnStr = columnSb.toString();
                columnStr = columnStr.replace("____javaType____", javaType);
                columnStr = columnStr.replace("____cName____", cName);
                columnStr = columnStr.replace("____remarks____", remarks);
                columnSb = new StringBuffer(columnStr);

                String getSetStr = getSetSb.toString();
                getSetStr = getSetStr.replace("____cName____", cName);
                getSetStr = getSetStr.replace("____remarks____", remarks);
                getSetStr = getSetStr.replace("____ClassName____", titleCaseCname);
                getSetStr = getSetStr.replace("____javaType____", javaType);
                getSetSb = new StringBuffer(getSetStr);
            }
            getVo.append("	}\n\n");

            getMap.append("		return map;\n");
            getMap.append("	}\n\n");

            isEmptySb.append("		return true;\n");
            isEmptySb.append("	}\n");

            voClassFileSb.append(columnSb);
            voClassFileSb.append("	@SuppressWarnings(\"unchecked\")\n");
            voClassFileSb.append("	public " + className + "(String json) {\n");
            voClassFileSb.append("		this(JSONObject.parseObject(json, Map.class));\n");
            voClassFileSb.append("	}\n\n");
            voClassFileSb.append(getSetSb);
            voClassFileSb.append("	public " + className + "() {}\n\n");
            voClassFileSb.append(getVo);
            voClassFileSb.append(getMap);
            voClassFileSb.append(isEmptySb);
            voClassFileSb.append("\n}");

            outFile(voClassFileSb.toString(), vo_Path + voName, className, "java");
        }
    }

    private String typeTransformation(String type) {
        if (type.equals("INT") || type.equals("TINYINT") || type.equals("SMALLINT")) {
            return "Integer";
        } else if (type.equals("VARCHAR") || type.equals("TEXT")) {
            return "String";
        } else if (type.equals("BIGINT")) {
            return "Long";
        } else if (type.equals("BIT")) {
            return "Boolean";
        } else if (type.equals("FLOAT")) {
            return "Float";
        } else if (type.equals("DOUBLE")) {
            return "Double";
        } else if (type.equals("TIMESTAMP")) {
            return "Timestamp";
        } else if (type.equals("DECIMAL") || type.equals("DECIMAL UNSIGNED")) {
            return "BigDecimal";
        } else if (type.equals("DATETIME") || type.equals("DATE")) {
            return "Date";
        }
        return type;
    }

    /**
     * 根据表名获取到表格的详细数据
     *
     * @param tableNames
     * @return
     * @throws SQLException
     */
    private List<Object> selectTalbeDetailed(String... tableNames) throws SQLException {
        List<Object> tableNameMaplist = new ArrayList<Object>();

        // 获得元数据
        DatabaseMetaData metaData = comboPooledDataSource.getConnection().getMetaData();
        // 获得表信息
        ResultSet tables = metaData.getTables(null, null, null, new String[]{"TABLE"});

        while (tables.next()) {
            // 获得表名
            String table_name = tables.getString("TABLE_NAME");

            boolean isCon = false;
            for (String tableName : tableNames) {
                if (tableName.equals(table_name)) {
                    isCon = false;
                    break;
                } else {
                    isCon = true;
                }
            }

            if (isCon) {
                continue;
            }

            // 通过表名获得所有字段名
            ResultSet columns = metaData.getColumns(null, null, table_name, "%");

            List<Object> list = new ArrayList<Object>();

            // 获得所有字段名
            while (columns.next()) {
                Map<String, String> columnNameMap = new HashMap<>(); // 保存字段名
                String column_name = columns.getString("COLUMN_NAME");
                String type_name = columns.getString("TYPE_NAME");
                String remarks = columns.getString("REMARKS");

                columnNameMap.put("type", type_name);
                columnNameMap.put("columnName", column_name);
                columnNameMap.put("remarks", remarks);

                list.add(columnNameMap);
            }
            Map<String, Object> tableNameMap = new HashMap<>();
            tableNameMap.put("tableName", table_name);
            tableNameMap.put("columnList", list);
            tableNameMaplist.add(tableNameMap);
        }
        return tableNameMaplist;
    }

    /**
     * 输出文件到指定目录文件夹
     *
     * @param code
     * @param path
     * @param fileName
     * @param fileType
     */
    private void outFile(String code, String path, String fileName, String fileType) {
        try {
            File file = new File(path + "//" + fileName + "." + fileType);

            if (!file.exists()) {
                new File(path).mkdirs();
                file.createNewFile();
            }
            OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
            BufferedWriter writer = new BufferedWriter(write);
            writer.write(code);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String nameTransformation(String name) {
        if (name.indexOf("T_") == 0 || name.indexOf("t_") == 0) {
            name = name.substring(2, name.length());
        }

        Pattern linePattern = Pattern.compile("_(\\w)");
        name = name.toLowerCase();
        Matcher matcher = linePattern.matcher(name);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        String string = sb.toString();

        return string;

    }

    /**
     * 首字母大写
     *
     * @param string
     * @return
     */
    private String titleCase(String string) {
        return string.substring(0, 1).toUpperCase() + string.substring(1, string.length());
    }
}
